package joomz;

import jbxml.Processor;
import jbxml.Registry;

import java.io.BufferedReader;
import java.util.List;
import javax.script.Bindings;

/**
 * Bean representation of document root element
 */
public class Game 
    extends Abstract
{

    public static class Invalid 
        extends RuntimeException
    {
        public Invalid(String msg){
            super(msg);
        }
    }

    /**
     * Bean representation of document abstract container context.
     */
    public static class Document
        extends Abstract
        implements jbxml.Component.Root
    {
        private java.net.URL base;

        public Document(){
            super();
        }


        public java.net.URL getDocumentBase(){
            return this.base;
        }
        public void setDocumentBase(java.net.URL url){
            this.base = url;
        }
        public java.net.URL getInputURL(String path)
            throws java.net.MalformedURLException
        {
            return this.getClass().getResource(path);
        }
        public java.io.InputStream getInputStream(String path)
            throws java.io.IOException
        {
            return this.getClass().getResourceAsStream(path);
        }
        public void init(Bindings bindings){
        }
        public boolean isValid(){
            return true;
        }
    }

    public final static jbxml.Dictionary<String,String> Resources = new jbxml.Properties();
    static {
        Resources.put("ns:joomz","joomz");
    }


    public Game(){
        super();
    }


    public boolean isValid(){
        if (0 < this.getItems().size() && 0 < this.getDirections().size() && 
            0 < this.getActions().size() && 0 < this.getMap().size() && 
            null != this.player && this.player.isValid())
        {
            for (Direction direction: this.directions){
                if (!direction.isValid())
                    return false;
            }

            for (Action action: this.actions){
                if (!action.isValid())
                    return false;
            }

            for (Location location: this.map){
                if (!location.isValid())
                    return false;
            }

            if (null == Actor.Set.forName("error"))
                throw new Game.Invalid("Game actors missing ERROR.");

            else if (null == Actor.Set.forName("prompt"))
                throw new Game.Invalid("Game actors missing PROMPT.");


            return true;
        }
        else
            throw new Game.Invalid("Game missing ITEMS or DIRECTIONS or ACTIONS or MAP or PLAYER.");
    }


    private final static Options.Option OptHelp = new Options.Option(new String[]{"-h", "-?", "--help"},
                                                                     "Print options.");
    private final static Options.Option OptGame = new Options.Option(new String[]{"-g", "--game"},
                                                                     "File or url game description XML.");
    private final static Options.Option OptTest = new Options.Option(new String[]{"-t", "--test"},
                                                                     "File or url input for debugging.");

    public static void main(String[] argv){
        Options opts = new Options(argv);
        if (opts.includes(OptHelp)){
            System.err.println("Usage");
            System.err.println("   Game ["+OptHelp.synopsis+"] [{"+OptGame.synopsis+"} file|url] [{"+OptTest.synopsis+"} file|url]");
            System.err.println("Options");
            System.err.println("   "+OptHelp.synopsis);
            System.err.println("       "+OptHelp.description);
            System.err.println("   "+OptGame.synopsis);
            System.err.println("       "+OptGame.description);
            System.err.println("   "+OptTest.synopsis);
            System.err.println("       "+OptTest.description);
            System.exit(1);
        }
        else {
            try {
                Game.Document doc = new Game.Document();
                java.net.URL url = opts.getUrl(OptGame,Game.class.getResource("/game.xml"));
                Processor resolver = new Processor(doc,Resources);
                Game game = (Game)resolver.readObject(url);
                Bindings gamespace = new Registry();
                resolver.enter();
                try {
                    game.init(gamespace);
                    if (game.isValid()){
                        String gameDesc = game.getDescription();
                        if (null != gameDesc){
                            String[] desc = jela.JavaCompiledScript.wraptext(gameDesc,60);
                            for (int cc =0, count = desc.length; cc < count; cc++){
                                System.out.printf(" %-60s\n",desc[cc]);
                            }
                        }

                        Player player = game.player;
                        gamespace.put("game",game);
                        gamespace.put("player",player);

                        BufferedReader lin = opts.openBufferedReader(OptTest,true);
                        String line;
                        String err = (Actor.Set.forName("error")).getDescription()+'\n';
                        String prompt = (Actor.Set.forName("prompt")).getDescription()+' ';
                        Action action;
                        List<Action> actionList;
                        Location location;
                        Actor actor;
                        Event event;
                        while (true){
                            System.out.print(prompt);
                            line = lin.readLine();
                            if (null == line){
                                line = "exit";
                            }

                            line = line.trim();
                            if (0 < line.length()){
                                try {
                                    action = new Action(line);
                                    location = player.getLocation();
                                    actionList = location.listActionFor(action);
                                    boolean ok = false;
                                    for (Action actl : actionList){
                                        actor = actl.getActor();
                                        if (null != actor){
                                            event = actor.getEvent();
                                            if (null != event){
                                                gamespace.put("location",location);
                                                gamespace.put("action",actl);
                                                gamespace.put("actor",actor);
                                                gamespace.put("event",event);
                                                try {
                                                    java.lang.Object re = event.invoke();
                                                    if (re instanceof Boolean){
                                                        if (((Boolean)re).booleanValue()){
                                                            ok = true;
                                                            break;
                                                        }
                                                    }
                                                }
                                                catch (jbxml.Function.InvokeErrorTarget exc){
                                                    System.err.printf("Error in <action id='%s'><actor id='%s'><event id='%s'/></actor></action>\n",actl.getId(),actor.getId(),event.getId());
                                                    System.err.println(exc.getCause().getMessage());
                                                }
                                                catch (Exception exc){
                                                    System.err.printf("Error in <action id='%s'><actor id='%s'><event id='%s'/></actor></action>\n",actl.getId(),actor.getId(),event.getId());
                                                    exc.printStackTrace();
                                                }
                                            }
                                            else
                                                System.out.printf("[BUG] Missing event for actor in <action id='%s'><actor id='%s'/></action>\n",actl.getId(),actor.getId());
                                        }
                                        else
                                            System.out.printf("[BUG] Missing actor for <action id='%s'/>\n",actl.getId());
                                    }
                                    if (!ok)
                                        System.out.printf(err,line);
                                }
                                catch (Action.Parse x){
                                    System.out.printf(err,line);
                                }
                            }
                        }
                    }
                    else {
                        System.err.println("Invalid game.");
                        System.exit(1);
                    }
                }
                finally {
                    resolver.exit();
                }
            }
            catch (Game.Invalid exc){
                System.err.println("Invalid game: "+exc.getMessage());
                System.exit(1);
            }
            catch (Exception exc){
                exc.printStackTrace();
                System.exit(1);
            }
        }
    }
}
